package com.example.kotlinTest

import android.icu.text.Transliterator
import java.util.*

//P 198 类与对象
/**
 * 所有对象相关的
 * 类与继承
 * 属性与字段
 * 接口
 * 函数式（SAM）接口 P216
 * 可见性修饰符
 * 扩展
 * 数据类型
 * 密封类
 * 泛型
 * 嵌套类
 * 枚举类
 * 对象
 * 类型别名
 * 委托
 * 委托属性
 *
 * 剩余内容：
 * 1.协程
 * 2.更多语言结构
 *
 */
class ClazzAndObj {
}

//类与继承
/**
类
Kotlin中使用关键字 class 声明类
 */
class Invoice{}
/**
 * 类声明由类名、 类头（ 指定其类型参数、 主构造函数等） 以及由花括号包围的类体构成。类头与类体都是可选的；
 * 如果一个类没有类体， 可以省略花括号。
 */
class Empty

/**
 * 在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。 主构造函数是类头
的一部分： 它跟在类名（ 与可选的类型参数） 后。

如果主构造函数没有任何注解或者可见性修饰符， 可以省略这个 constructor 关键字
 */
class Person0 constructor(firstName: String) { /*……*/ }
class Person1(firstName: String) { /*……*/ }

/**
 * 主构造函数不能包含任何的代码。 初始化的代码可以放到以 init 关键字作为前缀的初始化
块（ initializer blocks） 中。
在实例初始化期间， 初始化块按照它们出现在类体中的顺序执行， 与属性初始化器交织在一
起：
 */

/**
 * 主构造函数不能包含任何的代码。 初始化的代码可以放到以 init 关键字作为前缀的初始化
块（ initializer blocks） 中。
在实例初始化期间， 初始化块按照它们出现在类体中的顺序执行， 与属性初始化器交织在一
起：
 */
//sampleStart
class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)

    init {
        println("First initializer block that prints ${name}")
    }
    val secondProperty = "Second property: ${name.length}".also(::println)
    init {
        println("Second initializer block that prints ${name.length}")
    }

    val thirdProperty = "I have a try by $name".also { println(it) }
}
//sampleEnd
fun main() {
    InitOrderDemo("hello")
}

/**
 * 请注意， 主构造的参数可以在初始化块中使用。
 * 它们也可以在类体内声明的属性初始化器中使用：
 */
class Customer1(name: String) {
    val customerKey = name.toUpperCase(Locale.ROOT)
}
/**
 * 事实上， 声明属性以及从主构造函数初始化属性， Kotlin 有简洁的语法：
 */
class Person2(val firstName: String, val lastName: String, var age: Int) { /*……*/ }
/**
 * 声明类属性时， 可以使用尾部逗号：
 */
class Person(
    val firstName: String,
    val lastName: String,
    var age: Int // 尾部逗号,低版本不支持
) { /*...*/ }

/**
 * 与普通属性一样， 主构造函数中声明的属性可以是可变的（ var ） 或只读的（ val ） 。
如果构造函数有注解或可见性修饰符， 这个 constructor 关键字是必需的， 并且这些修饰符
在它前面：
 */
class Customer2 public /*@Inject*/ constructor(name: String) { /*……*/ }

/**
 * 次构造函数
 * 类也可以声明前缀有 constructor 的次构造函数：
 */
class Person3 {
    var children: MutableList<Person3> = mutableListOf()
    constructor(parent: Person3) {
        parent.children.add(this)
    }
}

/**
 * 如果类有一个主构造函数， 每个次构造函数需要委托给主构造函数， 可以直接委托或者通过
别的次构造函数间接委托。 委托到同一个类的另一个构造函数用 this 关键字即可：
 */
class Person4(val name: String) {
    var children: MutableList<Person4> = mutableListOf()
    constructor(name: String, parent: Person4) : this(name) {
        parent.children.add(this)
    }
}

/**
 * 请注意， 初始化块中的代码实际上会成为主构造函数的一部分。 委托给主构造函数会作为次
构造函数的第一条语句， 因此所有初始化块与属性初始化器中的代码都会在次构造函数体之
前执行。 即使该类没有主构造函数， 这种委托仍会隐式发生， 并且仍会执行初始化块：
 */
//sampleStart
class Constructors {
    init {
        println("Init block")
    }
    constructor(i: Int) {
        println("Constructor $i")
    }
}
//sampleEnd
//fun main() {
//    Constructors(1)
//}

/**
 * 如果一个非抽象类没有声明任何（ 主或次） 构造函数， 它会有一个生成的不带参数的主构造
函数。 构造函数的可见性是 public。 如果你不希望你的类有一个公有构造函数， 你需要声明一
个带有非默认可见性的空的主构造函数：
 */
class DontCreateMe private constructor () { /*……*/ }

/**
 * 注意： 在 JVM 上， 如果主构造函数的所有的参数都有默认值， 编译器会生成 一个额外的
无参构造函数， 它将使用默认值。 这使得 Kotlin 更易于使用像 Jackson 或者 JPA这样的通
过无参构造函数创建类的实例的库。
 */
class Customer3(val customerName: String = "")

/**
 * 创建类的实例
 * 要创建一个累的实例，我们就像普通函数一样调用构造函数：
 * val invoice = Invoice()
val customer = Customer("Joe Smith")
注意 Kotlin 并没有 new 关键字。
创建嵌套类、 内部类与匿名内部类的类实例在嵌套类中有述。
 */

/**
 * 类成员
 * 类可以包含：
构造函数与初始化块
函数
属性
嵌套类与内部类
对象声明
 */

/**
 * 继承
 * 在kotlin中所有类都有一个共同的超类Any，着对于没有超类型生命的类是默认超类：
 * Any 有三个方法： equals() 、 hashCode() 与 toString() 。 因此， 为所有 Kotlin 类都定义了
这些方法。
 */
class Example // 从 Any 隐式继承

/**
 * 默认情况下， Kotlin 类是最终（ final） 的： 它们不能被继承。 要使一个类可继承， 请用 open
关键字标记它。
 */
//open class Base // 该类开放继承

/**
 * 如需声明一个显式的超类型， 请在类头中把超类型放到冒号之后：
 */
open class Base(p: Int)
class Derived(p: Int) : Base(p)

/**
 * 如果派生类有一个主构造函数， 其基类可以（ 并且必须） 用派生类主构造函数的参数就地初
始化。
如果派生类没有主构造函数， 那么每个次构造函数必须使用 super 关键字初始化其基类型，
或委托给另一个构造函数做到这一点。 注意， 在这种情况下， 不同的次构造函数可以调用基
类型的不同的构造函数：
 */
//class MyView : View {
//    constructor(ctx: Context) : super(ctx)
//    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
//}

/**
 * 覆盖方法
我们之前提到过， Kotlin 力求清晰显式。 因此， Kotlin 对于可覆盖的成员（ 我们称之为开放）
以及覆盖后的成员需要显式修饰符：
 */
//open class Shape {
//    open fun draw() { /*……*/ }
//    fun fill() { /*……*/ }
//}
//class Circle() : Shape() {
//    override fun draw() { /*……*/ }
//}

/**
 * Circle.draw() 函数上必须加上 override 修饰符。 如果没写， 编译器将会报错。 如果函数没
有标注 open 如 Shape.fill() ， 那么子类中不允许定义相同签名的函数， 不论加不加
override。 将 open 修饰符添加到 final类（ 即没有 open 的类） 的成员上不起作用。
标记为 override 的成员本身是开放的， 也就是说， 它可以在子类中覆盖。 如果你想禁止再次
覆盖， 使用 final 关键字：
 */
//open class Rectangle() : Shape() {
//    final override fun draw() { /*……*/ }
//}

/**
 * 覆盖属性
属性覆盖与方法覆盖类似； 在超类中声明然后在派生类中重新声明的属性必须以 override 开
头， 并且它们必须具有兼容的类型。 每个声明的属性可以由具有初始化器的属性或者具有
get 方法的属性覆盖。
 */
//open class Shape {
//    open val vertexCount: Int = 0
//}
//class Rectangle : Shape() {
//    override val vertexCount = 4
//}

/**
 * 你也可以用一个 var 属性覆盖一个 val 属性， 但反之则不行。 这是允许的， 因为一个
val 属性本质上声明了一个 get 方法， 而将其覆盖为 var 只是在子类中额外声明一个
set 方法。
请注意， 你可以在主构造函数中使用 override 关键字作为属性声明的一部分。
 */
//interface Shape {
//    val vertexCount: Int
//}
//class Rectangle(override val vertexCount: Int = 4) : Shape // 总是有 4 个顶点
//class Polygon : Shape {
//    override var vertexCount: Int = 0 // 以后可以设置为任何数
//}

/**
 * 派生类初始化顺序
在构造派生类的新实例的过程中， 第一步完成其基类的初始化（ 在之前只有对基类构造函数
参数的求值） ， 因此发生在派生类的初始化逻辑运行之前。
这意味着， 基类构造函数执行时， 派生类中声明或覆盖的属性都还没有初始化。 如果在基类
初始化逻辑中（ 直接或通过另一个覆盖的 open 成员的实现间接） 使用了任何一个这种属
性， 那么都可能导致不正确的行为或运行时故障。 设计一个基类时， 应该避免在构造函数、
属性初始化器以及 init 块中使用 open 成员。
 */
//sampleStart
//open class Base(val name: String) {
//    init { println("Initializing Base") }
//    open val size: Int =
//        name.length.also { println("Initializing size in Base: $it") }
//}
//class Derived(
//name: String,
//val lastName: String,
//) : Base(name.capitalize().also { println("Argument for Base: $it") }) {
//    init { println("Initializing Derived") }
//    override val size: Int =
//        (super.size + lastName.length).also { println("Initializing size in Derived: $it"
//        ) }
//}
//sampleEnd
//fun main() {
//    println("Constructing Derived(\"hello\", \"world\")")
//    Derived("hello", "world")
//}

/**
 * 调用超类实现
派生类中的代码可以使用 super 关键字调用其超类的函数与属性访问器的实现：
 */
//open class Rectangle {
//    open fun draw() { println("Drawing a rectangle") }
//    val borderColor: String get() = "black"
//}
//class FilledRectangle : Rectangle() {
//    override fun draw() {
//        super.draw()
//        println("Filling the rectangle")
//    }
//    val fillColor: String get() = super.borderColor
//}

/**
 * 在一个内部类中访问外部类的超类， 可以通过由外部类名限定的 super 关键字来实
现： super@Outer ：
 */
open class Rectangle1 {
    open fun draw() { println("Drawing a rectangle") }
    val borderColor: String get() = "black"
}
//sampleStart
class FilledRectangle: Rectangle1() {
    override fun draw() {
        val filler = Filler()
        filler.drawAndFill()
    }
    inner class Filler {
        fun fill() { println("Filling") }
        fun drawAndFill() {
            super@FilledRectangle.draw() // 调用 Rectangle 的 draw() 实现
            fill()
            println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 所实现的 borderColor 的 get()
        }
    }
}
//sampleEnd
//fun main() {
//    val fr = FilledRectangle()
//    fr.draw()
//}

/**
 * 覆盖规则
在 Kotlin 中， 实现继承由下述规则规定： 如果一个类从它的直接超类继承相同成员的多个实
现， 它必须覆盖这个成员并提供其自己的实现（ 也许用继承来的其中之一） 。 为了表示采用
从哪个超类型继承的实现， 我们使用由尖括号中超类型名限定的 super ， 如 super<Base> ：

可以同时继承 Rectangle 与 Polygon ， 但是二者都有各自的 draw() 实现， 所以我们必须在
Square 中覆盖 draw() ， 并提供其自身的实现以消除歧义
 */
open class Rectangle2 {
    open fun draw() { /* …… */ }
}
interface Polygon {
    fun draw() { /* …… */ } // 接口成员默认就是“open”的
}
class Square() : Rectangle2(), Polygon {
    // 编译器要求覆盖 draw()：
    override fun draw() {
        super<Rectangle2>.draw() // 调用 Rectangle.draw()
        super<Polygon>.draw() // 调用 Polygon.draw()
    }
}

/**
 * 抽象类
类以及其中的某些成员可以声明为 abstract 。 抽象成员在本类中可以不用实现。 需要注意
的是， 我们并不需要用 open 标注一个抽象类或者函数——因为这不言而喻。
我们可以用一个抽象成员覆盖一个非抽象的开放成员
 */
open class Polygon1 {
    open fun draw() {}
}
abstract class Rectangle3 : Polygon1() {
    abstract override fun draw()
}

/**
 * 伴生对象
如果你需要写一个可以无需用一个类的实例来调用、 但需要访问类内部的函数（ 例如， 工厂
方法） ， 你可以把它写成该类内对象声明中的一员。
更具体地讲， 如果在你的类内声明了一个伴生对象， 你就可以访问其成员， 只是以类名作为
限定符。
 */

/**
 * 属性
声明属性
Kotlin 类中的属性既可以用关键字 var 声明为可变的， 也可以用关键字 val 声明为只读的。
 */
class Address {
    var name: String = "Holmes, Sherlock"
    var street: String = "Baker"
    var city: String = "London"
    var state: String? = null
    var zip: String = "123456"
}
/**
 * 要使用一个属性， 只要用名称引用它即可：
 */
fun copyAddress(address: Address): Address {
    val result = Address() // Kotlin 中没有“new”关键字
    result.name = address.name // 将调用访问器
    result.street = address.street
// ……
    return result
}

/**
 * Getters 与 Setters
声明一个属性的完整语法是

var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
 */
/**
 * 其初始器（ initializer） 、 getter 和 setter 都是可选的。 属性类型如果可以从初始器 （ 或者从其
getter 返回值， 如下文所示） 中推断出来， 也可以省略。
例如:
 */
//var allByDefault: Int? // 错误： 需要显式初始化器， 隐含默认 getter 和 setter
var initialized = 1 // 类型 Int、 默认 getter 和 setter
/**
 * 一个只读属性的语法和一个可变的属性的语法有两方面的不同：
 * 1、 只读属性的用 val 开始代替 var
 * 2、 只读属性不允许 setter
 */
//val simple: Int? // 类型 Int、 默认 getter、 必须在构造函数中初始化
val inferredType = 1 // 类型 Int 、 默认 getter
/**
 * 我们可以为属性定义自定义的访问器。 如果我们定义了一个自定义的 getter， 那么每次访问该
属性时都会调用它 （ 这让我们可以实现计算出的属性） 。 以下是一个自定义 getter 的示例：
 */
/*val isEmpty: Boolean
    get() = this.size == 0*/
/**
 * 如果我们定义了一个自定义的 setter， 那么每次给属性赋值时都会调用它。 一个自定义的 setter
如下所示：
 */
/*var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // 解析字符串并赋值给其他属性
    }*/
/**
 * 按照惯例， setter 参数的名称是 value ， 但是如果你喜欢你可以选择一个不同的名称。
自 Kotlin 1.1 起， 如果可以从 getter 推断出属性类型， 则可以省略它：
 */
//val isEmpty get() = this.size == 0 // 具有类型 Boolean
/**
 * 如果你需要改变一个访问器的可见性或者对其注解， 但是不需要改变默认的实现， 你可以定
义访问器而不定义其实现:
 */
/*var setterVisibility: String = "abc"
    private set // 此 setter 是私有的并且有默认实现
var setterWithAnnotation: Any? = null
    @Inject set // 用 Inject 注解此 setter*/

/**
 * 幕后字段
在 Kotlin 中， 字段仅在需要作为属性的一部分在内存中保存其值时使用。 字段不能直接声明。
然而， 当一个属性需要一个幕后字段时， Kotlin 会自动提供。 这个幕后字段可以使用 field
标识符在访问器中引用：
 */
var counter = 0 // 注意： 这个初始器直接为幕后字段赋值
    set(value) {
        if (value >= 0) field = value
    }

/**
 * field 标识符只能用在属性的访问器内。
如果属性至少一个访问器使用默认实现， 或者自定义访问器通过 field 引用幕后字段， 将会
为该属性生成一个幕后字段。
例如， 下面的情况下， 就没有幕后字段：
val isEmpty: Boolean
get() = this.size == 0
 */

/**
 * 幕后属性
如果你的需求不符合这套“隐式的幕后字段”方案， 那么总可以使用 幕后属性（ backing
property） ：
对于 JVM 平台： 通过默认 getter 和 setter 访问私有属性会被优化， 所以本例不会引入函
数调用开销
 */
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // 类型参数已推断出
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

/**
 * 编译期常量
如果只读属性的值在编译期是已知的， 那么可以使用 const 修饰符将其标记为编译期常量。
这种属性需要满足以下要求：
位于顶层或者是 object 声明 或 companion object 的一个成员
以 String 或原生类型值初始化
没有自定义 getter
这些属性可以用在注解中：
 */
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { /*……*/ }

/**
 * 延迟初始化属性与变量
一般地， 属性声明为非空类型必须在构造函数中初始化。 然而， 这经常不方便。 例如： 属性
可以通过依赖注入来初始化， 或者在单元测试的 setup 方法中初始化。 这种情况下， 你不能
在构造函数内提供一个非空初始器。 但你仍然想在类体中引用该属性时避免空检测。
为处理这种情况， 你可以用 lateinit 修饰符标记该属性：
 */
/*public class MyTest {
    lateinit var subject: TestSubject
    @SetUp fun setup() {
        subject = TestSubject()
    }
    @Test fun test() {
        subject.method() // 直接解引用
    }
}*/
/**该修饰符只能用于在类体中的属性（ 不是在主构造函数中声明的 var 属性， 并且仅当该属性
没有自定义 getter 或 setter 时） ， 而自 Kotlin 1.2 起， 也用于顶层属性与局部变量。 该属性或变
量必须为非空类型， 并且不能是原生类型。
在初始化前访问一个 lateinit 属性会抛出一个特定异常， 该异常明确标识该属性被访问及它
没有初始化的事实。*/

/**
 * 检测一个 lateinit var 是否已初始化（ 自 1.2 起）
要检测一个 lateinit var 是否已经初始化过， 请在该属性的引用上使用 .isInitialized ：
此检测仅对可词法级访问的属性可用， 即声明位于同一个类型内、 位于其中一个外围类型中
或者位于相同文件的顶层的属性。
 */
/*if (foo::bar.isInitialized) {
    println(foo.bar)
}*/

/**
 * 覆盖属性
参见覆盖属性
 */

/**
 * 委托属性
最常见的一类属性就是简单地从幕后字段中读取（ 以及可能的写入） 。 另一方面， 使用自定
义 getter 和 setter 可以实现属性的任何行为。 介于两者之间， 属性如何工作有一些常见的模
式。 一些例子： 惰性值、 通过键值从映射读取、 访问数据库、 访问时通知侦听器等等。
这些常见行为可以通过使用委托属性实现为库
 */

/**
 * 接口
Kotlin 的接口可以既包含抽象方法的声明也包含实现。 与抽象类不同的是， 接口无法保存状
态。 它可以有属性但必须声明为抽象或提供访问器实现。
使用关键字 interface 来定义接口
 */
interface MyInterface {
    fun bar()
    fun foo() {
// 可选的方法体
    }
}

/**
 * 实现接口
一个类或者对象可以实现一个或多个接口。
 */
class Child : MyInterface {
    override fun bar() {
// 方法体
    }
}

/**
 * 接口中的属性
你可以在接口中定义属性。 在接口中声明的属性要么是抽象的， 要么提供访问器的实现。 在
接口中声明的属性不能有幕后字段（ backing field） ， 因此接口中声明的访问器不能引用它们。
 */
interface MyInterface1 {
    val prop: Int // 抽象的
    val propertyWithImplementation: String
        get() = "foo"
    fun foo() {
        print(prop)
    }
}
class Child1 : MyInterface1 {
    override val prop: Int = 29
}
/**接口继承
一个接口可以从其他接口派生， 从而既提供基类型成员的实现也声明新的函数与属性。 很自
然地， 实现这样接口的类只需定义所缺少的实现：*/
interface Named {
    val name: String
}
interface Person5 : Named {
    val firstName: String
    val lastName: String
    override val name: String get() = "$firstName $lastName"
}
data class Employee(
// 不必实现“name”
    override val firstName: String,
    override val lastName: String,
    val position: Transliterator.Position
) : Person5

/**
 * 解决覆盖冲突
实现多个接口时， 可能会遇到同一方法继承多个实现的问题。 例如
上例中， 接口 A 和 B 都定义了方法 foo() 和 bar()。 两者都实现了 foo(), 但是只有 B 实现了
bar() (bar() 在 A 中没有标记为抽象， 因为在接口中没有方法体时默认为抽象） 。 因为 C 是一
个实现了 A 的具体类， 所以必须要重写 bar() 并实现这个抽象方法。
然而， 如果我们从 A 和 B 派生 D， 我们需要实现我们从多个接口继承的所有方法， 并指明 D
应该如何实现它们。 这一规则既适用于继承单个实现（ bar()） 的方法也适用于继承多个实现
（ foo()） 的方法
 */
interface A {
    fun foo() { print("A") }
    fun bar()
}
interface B {
    fun foo() { print("B") }
    fun bar() { print("bar") }
}
class C : A {
    override fun bar() { print("bar") }
}
class D : A, B {
    override fun foo() {
        super<A>.foo()
        //接口
        super<B>.foo()
    }
    override fun bar() {
        super<B>.bar()
    }
}











